package org.aj.common.web.file.service;


import io.minio.messages.DeleteError;
import org.aj.base.exception.BusException;
import org.aj.base.web.response.ResponseResult;
import org.aj.common.web.file.bean.*;
import org.aj.common.web.file.util.DistributeFileUtils;
import org.aj.common.web.file.util.ImageUtil;
import org.aj.distribute.file.client.DistributeFileClient;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @description
 * @author aj
 * date  2022/1/2 22:45
 */
public class FileUploadBaseServiceImpl {


    private static final Logger log = LoggerFactory.getLogger(FileUploadBaseServiceImpl.class);
    private final DistributeFileClient distributeFileClient;

    private final FileCheckService fileCheckService;

    public FileUploadBaseServiceImpl(DistributeFileClient distributeFileClient, FileCheckService fileCheckService) {
        this.distributeFileClient = distributeFileClient;
        this.fileCheckService = fileCheckService;
    }



    public FileRetWrap uploadFile(FileVo fileVo)throws Exception {

        //处理掉前缀
        byte[] bytes;
        try {
            bytes = DistributeFileUtils.base64ToByte(fileVo.getBase64File());
        } catch (Exception e) {
            if( e instanceof IllegalArgumentException){
                //由于是用户导致的行为，除非是恶意上传才会触发 故日志级别设置info 错误信息简单打印，若要看详细信息可以在本地调试
                //或者远程debug一波
                log.info("传入的base64不符合要求"+e.getMessage());
                throw new BusException("base64不符合要求");
            }
            throw e;
        }

        //看能否拿到文件类型
        String contentType = DistributeFileUtils.getFileTypeFromBase64(fileVo.getBase64File());

        FileMetadata fileMetadata = new FileMetadata().setFileName(fileVo.getFileName())
                .setBeforeRelativePath(fileVo.getBeforeRelativePath())
                .setBytes(bytes)
                .setNeedKeepOriginalName(fileVo.getNeedKeepOriginalName())
                .setSubFolderPath(fileVo.getType())
                .setContentType(contentType);

        FileRetWrap fileRetWrap = distributeUpload(fileMetadata);

        return fileRetWrap;
    }





    /**
     * description  上传到分布式文件系统里面去
     * @param fileMetadata
     * @return: com.iotimc.yimi.base.bean.reqrsp.FileRetWrap
     * @author: jcj
     * date : 2020/12/4 14:22
     */
    public FileRetWrap distributeUpload(FileMetadata fileMetadata) throws Exception {

        if(StringUtils.isBlank(fileMetadata.getFileName()) || StringUtils.isBlank(fileMetadata.getSubFolderPath()) || null == fileMetadata.getBytes() || fileMetadata.getBytes() .length == 0 ){
            throw new IllegalArgumentException("fileName、path must not empty bytes must not null and must not empty");
        }

        byte[] bytes = validateFileByPath(fileMetadata);

        fileMetadata.setBytes(bytes);

        String beforeRelativePath = fileMetadata.getBeforeRelativePath();
        if(StringUtils.isNotBlank(beforeRelativePath)){
            String destFilePath = beforeRelativePath;

            //则直接覆盖掉之前生成的即可
            distributeFileClient.uploadFile(bytes,removeBucketName(destFilePath),fileMetadata.getContentType());
            //绝对路径
            String absPath = distributeFileClient.getUrlRoot()+ destFilePath;
            return FileRetWrap.builder().setRelativePath(destFilePath).setAbsolutePath(absPath);
        }

        //建立日期子文件夹
        String dateFormatStr = DateTimeFormatter.ofPattern("/yyyy/MM/dd/").format(LocalDateTime.now());
        String destFilePath;
        if(null != fileMetadata.getNeedKeepOriginalName() && fileMetadata.getNeedKeepOriginalName() ){
            //若需要保留原始文件名则在用一个唯一的子文件名称包裹即可
            destFilePath = fileMetadata.getSubFolderPath() + dateFormatStr+ DistributeFileUtils.generatePhotoName()+"/"+fileMetadata.getFileName();
        }else{
            //得到文件后缀
            String suffix = StringUtils.substringAfterLast(fileMetadata.getFileName(), ".");
            if(StringUtils.isBlank(suffix)){
                /**
                 *加入这段代码的含义是因为，目前前端的操作会把文件类型变成blob,然后导致获取文件后缀名的时候变成
                 * blob
                 */
                if("blob".equalsIgnoreCase(fileMetadata.getFileName()) && StringUtils.isNotBlank(fileMetadata.getContentType())){
                    suffix =  fileMetadata.getContentType().split("/")[1];
                }
            }

            //生成新的文件名称防止重复
            String newName;
            if(StringUtils.isBlank(suffix)){
                newName = DistributeFileUtils.generatePhotoName();
            }else{
                newName = DistributeFileUtils.generatePhotoNameAndSuffix("."+suffix);
            }
            //得到需要存放文件的存放路径
            destFilePath =  fileMetadata.getSubFolderPath()  + dateFormatStr+newName;
        }


        //上传文件
        distributeFileClient.uploadFile(bytes, destFilePath,fileMetadata.getContentType());
        //得到相对路径
        String relativePath = distributeFileClient.getBucketName() + "/" + destFilePath;
        //绝对路径
        String absPath = distributeFileClient.getUrlRoot()+relativePath;
        FileRetWrap build = FileRetWrap.builder().setRelativePath(relativePath).setAbsolutePath(absPath);


        return build;

    }

    /**
     * 移除掉相对路径包含的存储桶 约定字符串形式如下
     * 存储桶名称/子路径 ，
     * @param destFilePath
     * @return 若不是则原样返回 否则返回去除掉存储桶的路径
     */
    private String removeBucketName(String destFilePath) {

        if(destFilePath.contains("/") && destFilePath.length() >= 3){
        int endIndex = destFilePath.indexOf("/");
            //拿到存储桶
            String customBucketName = destFilePath.substring(0, endIndex);
            //得到子路径 不包含 "/"
            return  destFilePath.substring(destFilePath.indexOf(customBucketName)+customBucketName.length()+1);
        }
        return destFilePath;
    }

    /**
     * description 校验文件是否符合要求
     * @author aj
     * date  2022/1/2 17:05
     * @param fileMetadata 文件名称
     * @return byte[] 若检验不符合要求则抛出业务异常 com.iotimc.yimi.base.bean.exception.BusException
     * 若需要压缩则返回压缩后的字节大小
     */
    private byte[] validateFileByPath(FileMetadata fileMetadata) throws Exception {
        String path = fileMetadata.getSubFolderPath();
        if(!fileCheckService.checkFilePath(path)){
            throw new BusException("type 不符合约定");
        }
        //校验子文件夹长度、层级
        if(path.length() > 90){
            throw new BusException("子文件夹长度超出最大长度90");
        }

        String[] childPath = path.split("/");
        if(childPath.length > 3){
            throw new BusException("子文件夹层级超过最大层级3");
        }
        //校验命名规则
        String reg = "^[a-zA-Z0-9][a-zA-Z0-9\\.\\-]+[a-zA-Z0-9]$";
        for (String s : childPath) {
            if(!s.matches(reg)){
                throw new BusException("子文件夹不符合命名规范");
            }
        }

        FileCheckRule checkRule = fileCheckService.getCheckRule(path);

        byte[] bytes = fileMetadata.getBytes();
        if(null != checkRule){
            //校验大小
            Long fileMaxSize = checkRule.getFileMaxSize();

            if(null != fileMaxSize) {
                if (bytes.length > fileMaxSize) {

                    throw new BusException("文件超过最大值" + checkRule.getFileMaxSize());
                }
            }

            //校验文件类型
            String[] fileTypeArr = checkRule.getFileTypeArr();
            String contentType = fileMetadata.getContentType();
            if(null != fileTypeArr && fileTypeArr.length > 0 &&StringUtils.isNotBlank(contentType)){

                if(!ArrayUtils.contains(fileTypeArr,contentType)
                        && !ArrayUtils.contains(fileTypeArr,contentType.toLowerCase())){
                    throw new BusException("不支持的文件格式");
                }
            }

            String[] fileSuffixArr = checkRule.getFileSuffixArr();
            //文件后缀
            String fixed = StringUtils.substringAfterLast(fileMetadata.getFileName(), ".");
            if(null != fileSuffixArr && fileSuffixArr.length > 0 ){

                if(StringUtils.isBlank(fixed)){
                    throw new BusException("文件名称不符合规范");
                }
                if(!ArrayUtils.contains(fileSuffixArr,fixed)&&!ArrayUtils.contains(fileSuffixArr,fixed.toLowerCase())){
                    throw new BusException("不支持的文件后缀");
                }
            }

            CompressProperty compressProperty = checkRule.getCompressProperty();
            if(null != compressProperty){
                //若需要压缩则进行压缩
                Long compressThreshold = compressProperty.getCompressThreshold();

                String compressAfterSuffix = compressProperty.getCompressAfterSuffix();
                if(null != compressThreshold && bytes.length > compressThreshold){
                    Integer compressAfterMaxPixel = compressProperty.getCompressAfterMaxPixel();
                    while (bytes.length > compressThreshold){
                        bytes = ImageUtil.transformer(bytes, compressAfterMaxPixel,StringUtils.isBlank(compressAfterSuffix)?fixed:compressAfterSuffix);
                        compressAfterMaxPixel = (int) (compressAfterMaxPixel*0.5);
                    }

                }
            }

        }
        return bytes;
    }


    public ResponseResult<List<DeleteError>> delFiles(Set<String> destFilePathArr) throws Exception {


        List<String> destFilePathList = destFilePathArr.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return removeBucketName(s);
            }
        }).collect(Collectors.toList());
       List<DeleteError> listResponseResult = distributeFileClient.delFiles(destFilePathList, distributeFileClient.getBucketName());
        List<DeleteError> result = listResponseResult;
        if(null == result || result.isEmpty()){
            return ResponseResult.success("success");
        }
        return ResponseResult.success("success",listResponseResult);
    }
}
